package wsi.dataguide.tree;

import java.util.ArrayList;
import java.util.List;

import org.rygh.core.model.common.MapList;
import org.rygh.core.util.counter.SimpleCounter;
import org.rygh.core.xml.AbstractXmlLeaf;
import org.rygh.core.xml.XmlPath;

import wsi.match.SubtreeController;

public class WSXmlLeaf extends AbstractXmlLeaf<WSXmlObject, WSXmlLeaf> {

	public WSXmlLeaf(WSXmlLeaf parent, WSXmlObject object) {
		super(parent, object);
	}

	public boolean containsLeafEndsWith(XmlPath path) {
		List<WSXmlLeaf> leaves = getAllLeaves();
		boolean mapped = false;

		for (WSXmlLeaf l : leaves)
			if (path.subsumesWithoutIndex(l.getXpath())) {
				mapped = true;
				break;
			}

		return mapped;
	}

	/**
	 * collect all nodes starting from this node to the list
	 * 
	 * @param left
	 */
	private void collect(WSXmlLeaf node, WSXmlLeaf master,
			MapList<WSXmlLeaf, WSXmlLeaf> map) {
		if (master == null)
			return;

		if (map == null)
			return;

		map.put(master, node);

		if (!node.isLeaf())
			for (WSXmlLeaf i : node.getChildren())
				collect(i, master, map);

	}

	/**
	 * only applicable to data guide as the invoking variable. i.e. for any
	 * children list of a node in the left tree, there is only one node with a
	 * unique name. no more than one nodes have the same name.
	 * 
	 * @param otherNode
	 * @param left
	 * @param master
	 *            node in the right tree (non-dataguide tree)
	 * @param subtreeController
	 * @param right
	 */
	@Deprecated
	public void contains(WSXmlLeaf otherNode, WSXmlLeaf master,
			SubtreeController subtreeController,
			MapList<WSXmlLeaf, WSXmlLeaf> left,
			MapList<WSXmlLeaf, WSXmlLeaf> right) {
		if (!value.getName().equals(otherNode.getValue().getName())) {
			// assert false : String.format("%s vs. %s", value.getName(),
			// otherNode.getValue().getName());

			collect(this, master, left);
			collect(otherNode, master, right);

			// System.out.printf("!! collect left %s\n", this);
			// System.out.printf("!! collect right %s\n", node);

			return;
		}

		// when the tag names are the same

		// System.out.printf("compare left %s\n       right %s\n"
		// + "        left right sizes (%d, %d)\n\n", this, otherNode,
		// left != null ? left.size() : -1, right != null ? right.size()
		// : -1);

		if (isLeaf()) {
			if (otherNode.isLeaf()) {
				return;
			} else {
				for (WSXmlLeaf l : otherNode.children)
					collect(l, master, right);

				// System.out.printf("collect right %d children of %s\n",
				// otherNode.children.size(), otherNode);

				return;
			}

		} else {
			if (otherNode.isLeaf()) {
				for (WSXmlLeaf l : children)
					collect(l, master, left);

				// System.out.printf("collect left %s children of %s\n",
				// children.size(), this);

				return;
			} else {
				// System.out.println("Neither leaf stage.");

				List<WSXmlLeaf> rightChildren = new ArrayList<>(
						otherNode.children);

				List<WSXmlLeaf> otherSameNameChildren;
				int child_size = 0;
				boolean foundMaster = false;
				String majorMasterTag = null;
				SimpleCounter<String> tagCounter = new SimpleCounter<>();

				for (WSXmlLeaf l : otherNode.children)
					if (!l.getValue().isAttribute())
						child_size++;

				for (WSXmlLeaf l : children) {
					otherSameNameChildren = otherNode.getChildrenByName(l
							.getValue().getName());

					// System.out.printf("Children size %d, %d\n",
					// otherSameNameChildren.size(), child_size);

					/*
					 * for discogs, the master node is "results", but of 51
					 * children, the last one is not "results", but
					 * "pagination". thus we should soften the criterion and
					 * allow a small difference.
					 */

					int x, y;
					x = otherSameNameChildren.size();
					y = child_size;

					if (x == y || (1. * Math.abs(x - y) / x < .1)) {
						boolean allNonLeaf = true;

						if (otherSameNameChildren.isEmpty())
							allNonLeaf = false;

						for (WSXmlLeaf j : otherSameNameChildren) {
							tagCounter.count(j.getValue().getName());

							if (j.isLeaf()) {
								allNonLeaf = false;
								break;
							}
						}

						if (allNonLeaf) {
							if (l.getValue().getOccurence().getMax() > 1) {
								/*
								 * no parent node with max larger than 1
								 */

								boolean noNonOneMaxParent = true;

								WSXmlLeaf _l = l;

								do {
									_l = _l.getParent();

									if (_l.getValue().getOccurence().getMax() > 1) {
										noNonOneMaxParent = false;
										break;
									}
								} while (_l.getParent() != null);

								if (noNonOneMaxParent) {
									// System.out.println("FOUND MASTER " + l);
									foundMaster = true;
									majorMasterTag = tagCounter.getMaxObject();

									/*
									 * with artist-list/artist, master is the
									 * artist
									 */
									if (subtreeController.getCommonRoot() == null)
										subtreeController.setCommonRoot(l);
									else {
										assert l == subtreeController
												.getCommonRoot() : String
												.format("%s <> %s",
														l.getXpath(),
														subtreeController
																.getCommonRoot()
																.getXpath());
									}
								}
							}
						}
					}

					if (otherSameNameChildren.isEmpty()) {
						collect(l, master, left);

						// System.out.printf("collect left %s\n", l);
					} else {
						rightChildren.removeAll(otherSameNameChildren);
						WSXmlLeaf _master;

						for (WSXmlLeaf j : otherSameNameChildren) {
							_master = master;

							if (foundMaster
									&& j.getValue().getName()
											.equals(majorMasterTag)) {
								_master = j;
								subtreeController.addSubroot(_master);

								// System.out.printf("ADD MASTER [%s] @ %s\n",
								// _master.getValue().getValue(),
								// _master.getXpath());
							}

							l.contains(j, _master, subtreeController, left,
									right);
						}
					}
				}

				if (!rightChildren.isEmpty()) {
					for (WSXmlLeaf i : rightChildren)
						collect(i, master, right);

					// System.out.printf(
					// "collect right %d remaining children of %s\n",
					// rightChildren.size(), otherNode);
				}
			}
		}
	}

	public int getLeaveOccurrenceCount() {
		int count = 0;

		int max = getValue().getOccurence().getMax();

		if (isLeaf())
			count = max;
		else {
			for (WSXmlLeaf child : children)
				count += child.getLeaveOccurrenceCount();

			count *= max;
		}

		return count;
	}
}
